package com.hp.bon.sgw.wh.channel;

import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;

import com.hp.bon.sgw.wh.channel.common.BizConsts;
import com.hp.bon.sgw.wh.channel.especial.SpecialApiBizProcedure;
import com.hp.bon.sgw.wh.channel.outside.OutApiBizProcedure;
import com.hp.bon.sgw.wh.mml.parser.XmlMessageParserInfo;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.simpleframework.http.Request;
import org.simpleframework.http.Response;
import org.simpleframework.http.core.Container;
import org.simpleframework.http.core.ContainerProcessor;
import org.simpleframework.http.core.ContainerSelector;
import org.simpleframework.http.core.ContainerServer;
import org.simpleframework.transport.connect.Connection;
import org.simpleframework.transport.connect.SocketConnection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hp.bon.sgw.Constants.NodeStatus;
import com.hp.bon.sgw.bean.XmlMessage;
import com.hp.bon.sgw.core.BuzProcessorManager;
import com.hp.bon.sgw.core.CallParam;
import com.hp.bon.sgw.core.channel.IChannel;
import com.hp.bon.sgw.domain.Node;
import com.hp.bon.sgw.domain.SGWChannel;
import com.hp.bon.sgw.service.ConfigListener;
import com.hp.bon.sgw.service.ConfigService;
import com.hp.bon.sgw.start.SpringContext;
import com.hp.bon.sgw.wh.Constants.BlackCardParam;
import com.hp.bon.sgw.wh.bean.BillInfomation;
import com.hp.bon.sgw.wh.bean.BlackCardMsgIn;
import com.hp.bon.sgw.wh.channel.handler.BlackCardHandler;
import com.hp.bon.sgw.wh.service.BlackCardConfigService;
import com.hp.bon.sgw.wh.utils.XmlUtils;

import javax.xml.stream.XMLInputFactory;

/**
 * 
 * @ClassName: BlackCardChannel   
 * @Description: 黑卡专项治理接口(网厅接入)
 * @author: Sue  
 * @date:2014年11月20日 下午5:47:51
 */
public class BlackCardChannel implements Container, ConfigListener, IChannel {
	private static final Logger logger = LoggerFactory.getLogger(TaobaoChannel.class);
	private ContainerServer server = null;
	private SGWChannel channel;
	protected ConfigService confSrv;
	private MultiThreadedHttpConnectionManager connectionManager;
	private BlackCardConfigService blackCardConfigService;
	private SpecialApiBizProcedure specialApiBizProcedure;
	static XMLInputFactory inputFactory = XMLInputFactory.newInstance();
	public BlackCardChannel(SGWChannel channel) {
		this.channel = channel;
		confSrv = SpringContext.getInstance().getConfigService();
		setBlackCardConfigService(SpringContext.getApplicationContext().getBean(BlackCardConfigService.class));
	}

	@Override
	public SGWChannel getSGWChannel() {
		return this.channel;
	}

	@Override
	public int getThreadPoolSize() {
		return executor.getPoolSize();
	}

	@Override
	public int getThreadPoolQueueSize() {
		return executor.getQueue().size();
	}

	@Override
	public int getThreadPoolActiveCount() {
		return executor.getActiveCount();
	}

	@Override
	public int getNodeSendQueueSize(String nodeHostId) {
		return 0;
	}

	ThreadPoolExecutor executor;
	ThreadPoolExecutor collector;
	private static String blackCardQuery = "blackCardQuery";

	@Override
	public void start() throws IOException {

		specialApiBizProcedure = SpecialApiBizProcedure.instance();
		logger.info("outApiBizProcedure init successfull.");


		logger.info("start BlackCard channel:" + channel.toString() + " ...");
		int maxThreads = confSrv.getIntValue(BlackCardParam.SGW_THREAD_MAX, 20);
		logger.info(BlackCardParam.SGW_THREAD_MAX + " :" + maxThreads);
		
		logger.info("load blackcard Properties...");
//		PropertiesHelper.loadBlackCardProperties();
		
		String endPoint = this.channel.getUri();
		if (endPoint == null) {
			logger.warn("channel dont't defined uri");
			return;
		}
		logger.info("start sgw BlackCardChannel server portal at " + endPoint);

		try {
			URL srvURL = new URL(endPoint);
			int port = srvURL.getPort();
			//找不到端口就启动失败
			if (port == -1) {
				logger.info("start BlackCard Channel error : port Not defined");
				return ;
			}
			String host = srvURL.getHost();
			if (host.equals("127.0.0.1") || host.equalsIgnoreCase("localhost")) {
				host = "0.0.0.0";
			}
			server = new ContainerServer(this, maxThreads);

			@SuppressWarnings("resource")
			Connection connection = new SocketConnection(server);
			SocketAddress address = new InetSocketAddress(host, port);
			connection.connect(address);
			collector = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getCollect().getQueue();
			executor = ((ContainerSelector) ((ContainerProcessor) server.getProcessor()).getSelector()).getExecutor().getQueue();
			connectionManager = new MultiThreadedHttpConnectionManager();
			
			this.channel.setStatus(NodeStatus.ON_LINE);
			confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.ON_LINE);
		} catch (IOException e) {
			logger.error("start Taobao channel " + this.channel.toString()+ " err " + e);
		}
	}

	@Override
	public void stop() {
		logger.info("stop BlackCard channel");
		if (server != null) {
			try {
				server.stop();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if (connectionManager != null) {
			connectionManager.shutdown();
		}

		this.channel.setStatus(NodeStatus.OFF_LINE);
		confSrv.updateChannelStatus2DB(this.channel.getId(),NodeStatus.OFF_LINE);
		
		try {
			logger.info("wait 3s to exit...");
			Thread.sleep(3*1000);
		} catch (InterruptedException e) {
			logger.warn("Thread.sleep InterruptedException:",e);
		}
	}
	
	@Override
	public void handle(Request request, Response response) {
		logger.info(" black card hand receive request from " + request.getClientAddress() + "; "	+ request.toString());
		String requestPath = request.getPath().getPath();
		
		//黑卡查询
		if (requestPath.endsWith(blackCardQuery)) {



			long receiveTime = System.currentTimeMillis();

			PrintStream body = null;
			try {

				XmlMessage reqXmlMessage = null;
				reqXmlMessage = XmlMessageParserInfo.parseWSRequest(inputFactory, request.getContent());
				reqXmlMessage.setMsgTime(receiveTime);
				reqXmlMessage
						.setIp(request.getClientAddress() != null ? request
								.getClientAddress().getAddress()
								.getHostAddress() : "");

				// 检查是否走新流程，并处理新流程，若返回[TYPE_ORIG_FLOW]则表示走老流程
				logger.info("start check new flow...");
				logger.info("doProcedure...");
				String flowRetCode = specialApiBizProcedure.doProcedure(reqXmlMessage, request, response, receiveTime);
				if(!BizConsts.TYPE_ORIG_FLOW.equals(flowRetCode)) {
					logger.info("working on the new flow, and completely.");
					return;
				}


				body = response.getPrintStream();
				response.set("Content-Type", "text/xml;charset=UTF-8");
				response.set("Server", "HP Asyn Server )");
				response.setDate("Date", receiveTime);
				response.setDate("Last-Modified", receiveTime);
				String xml = IOUtils.toString(request.getInputStream(),Charset.forName("utf-8"));
				if (logger.isInfoEnabled()) {
					logger.info("handle request msg :"+xml);
				}
				if (xml != null) {
					String areaCode = "";
					String accNbr = "";
					String billingMode = "";
					String uid = "";
					String mid = "";
					String startTime = "";
					String endTime = "";
					List<BillInfomation> billInformations = new ArrayList<BillInfomation>();
					
					Document doc = null;
					try {
						doc = DocumentHelper.parseText(xml);
					} catch (DocumentException e) {
						if (logger.isDebugEnabled()) {
							logger.debug(" Service_Information 标签不存在");
						}
						creatErrAnswer(body,"",11009,null,null,"消息包不可识别");
					}
					Element root = doc.getRootElement();
					org.dom4j.Node dataNode =root.selectSingleNode("//DATA");
					uid = root.selectSingleNode("//UID").getText();
					mid = root.selectSingleNode("//MID").getText();					
					String dataStr = dataNode.getStringValue();
					if (logger.isInfoEnabled()) {
						logger.info(" Service_Information data CDATA:"+dataStr);
					}
					
					Document dataDoc = null;
					try {
						//验证Service_Information节点存在性
						dataDoc = DocumentHelper.parseText(dataStr);
					} catch (DocumentException e) {
						if (logger.isDebugEnabled()) {
							logger.debug("字段或节点未定义: Service_Information");
						}
						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义: Service_Information");
					}
					
					//验证Service_Information节点存在性
					org.dom4j.Node serviceNode = dataDoc.selectSingleNode("//Service_Information");
					if (serviceNode == null) {
						if (logger.isDebugEnabled()) {
							logger.debug(" 字段或节点未定义:Service_Information");
						}
						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义:Service_Information");
						return;
					}
					
					//验证Area_Code节点存在性
					org.dom4j.Node areaCodeNode = dataDoc.selectSingleNode("//Service_Information/Area_Code");
					if (areaCodeNode == null) {
						if (logger.isDebugEnabled()) {
							logger.debug("字段或节点未定义:Service_Information/Area_Code");
						}
						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义:Service_Information/Area_Code");
						return;
					}
					else if (StringUtils.isEmpty(areaCodeNode.getStringValue())) {
						//验证Area_Code节点必填性
						if (logger.isDebugEnabled()) {
							logger.debug("缺少必填字段:Service_Information/Area_Code");
						}
						creatErrAnswer(body,mid,11104,null,null,"缺少必填字段:Service_Information/Area_Code");
						return;
					}
					
					//验证Acc_Nbr节点存在性
					org.dom4j.Node accNbrNode = dataDoc.selectSingleNode("//Service_Information/Acc_Nbr");
					if (accNbrNode == null) {
						if (logger.isDebugEnabled()) {
							logger.debug("字段或节点未定义 :Service_Information/Acc_Nbr");
						}
						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义 :Service_Information/Acc_Nbr");
						return;
					}
					else if (StringUtils.isEmpty(accNbrNode.getStringValue())) {
						//验证Acc_Nbr节点必填性
						if (logger.isDebugEnabled()) {
							logger.debug("缺少必填字段:Service_Information/Acc_Nbr");
						}
						creatErrAnswer(body,mid,11104,null,null,"缺少必填字段:Service_Information/Acc_Nbr");
						return;
					}
					
//					//验证billingMode节点存在性
//					org.dom4j.Node billingModeNode = dataDoc.selectSingleNode("//Service_Information/BillingMode");
//					if (billingModeNode == null) {
//						if (logger.isDebugEnabled()) {
//							logger.debug("字段或节点未定义 :Service_Information/Acc_Nbr");
//						}
//						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义 :Service_Information/BillingMode");
//						return;
//					}
//					else if (StringUtils.isEmpty(billingModeNode.getStringValue())) {
//						//验证Acc_Nbr节点必填性
//						if (logger.isDebugEnabled()) {
//							logger.debug("空值字段:Service_Information/BillingMode");
//						}
//					}
					
					//验证Bill_Information节点存在性
					@SuppressWarnings("unchecked")
					List<Element> billElements = dataDoc.selectNodes("//Service_Information/Bill_Information");
					if (billElements == null || billElements.isEmpty()) {
						if (logger.isDebugEnabled()) {
							logger.debug("字段或节点未定义 :Service_Information/Bill_Information");
						}
						creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义 :Service_Information/Bill_Information");
						return;
					}
					else {
						for(Element ckmdnElement:billElements){
							org.dom4j.Node  ckmdnNode= ckmdnElement.selectSingleNode("//CkMDN");
							//验证CkMDN节点存在性
							if (ckmdnNode == null) {
								if (logger.isDebugEnabled()) {
									logger.debug("字段或节点未定义 :Service_Information/Bill_Information/CkMDN");
								}
								creatErrAnswer(body,mid,11106,null,null,"字段或节点未定义 :Service_Information/Bill_Information/CkMDN");
								return;
							}
							//验证CkMDN节点必填
							if (StringUtils.isEmpty(ckmdnNode.getStringValue())) {
								if (logger.isDebugEnabled()) {
									logger.debug("缺少必填字段 :Service_Information/Bill_Information/CkMDN");
								}
								creatErrAnswer(body,mid,11104,null,null,"缺少必填字段:Service_Information/Bill_Information/CkMDN");
								return;
							}
						}
					}
					
					areaCode = XmlUtils.getNodevalue(dataDoc,"//Service_Information/Area_Code");
					accNbr =   XmlUtils.getNodevalue(dataDoc,"//Service_Information/Acc_Nbr");
					startTime = XmlUtils.getNodevalue(dataDoc,"//Service_Information/StartTime");
					endTime =   XmlUtils.getNodevalue(dataDoc,"//Service_Information/EndTime");
					org.dom4j.Node billingModeNode = dataDoc.selectSingleNode("//Service_Information/Acc_Nbr");
					if (billingModeNode != null) {
						billingMode =   XmlUtils.getNodevalue(dataDoc,"//Service_Information/BillingMode");
						logger.info("billingMode:aaa"+billingMode);
					}else{
						billingMode ="";
					}
					
					
					
					
					if (logger.isInfoEnabled()) {
						logger.info("uid:"+uid);
						logger.info("mid:"+mid);
						logger.info("areaCode:"+areaCode);
						logger.info("accNbr:"+accNbr);
						logger.info("billingMode:"+billingMode);
					}
					
					@SuppressWarnings("unchecked")
					List<org.dom4j.Node> ckmdnlist = dataDoc.selectNodes("//Service_Information/Bill_Information");
					for(org.dom4j.Node node:ckmdnlist){
						if (StringUtils.isNotBlank(XmlUtils.getNodevalue(node,"CkMDN"))) {
							billInformations.add(new BillInfomation(XmlUtils.getNodevalue(node,"CkMDN")));
						}
					}
					
					Element serviceInformation = DocumentHelper.createElement("Service_Information");
					Element element = null;
					element = serviceInformation.addElement("Area_Code");
					element.setText(areaCode);
					element = serviceInformation.addElement("Value");
					element.setText(accNbr);
					//{ValueType}	1	查询值类型：1-业务号码；2-客户标识；3-合同号标识  我们选择填 :1 
					element = serviceInformation.addElement("ValueType");
					element.setText("1");
					//｛QueryType｝	1	查询类型（1：查询最新记录，2：查询所有记录）我们选择填1
					element = serviceInformation.addElement("QueryType");
					element.setText("1");

					// TODO 增加IP校验
					String ip = request.getClientAddress().getAddress().getHostAddress();
					// 将请求消息转换成DCC消息对应格式的XmlMessage
					XmlMessage req = new XmlMessage(true);
					req.setIp(ip);
					req.setMsgTime(receiveTime);
					req.setMID(mid);
					req.setUID(uid);
					req.setSCID(BlackCardParam.USERINFOQUERY_HB);
					req.setSIG(null);
					req.setREPTAG(0);
					req.setDATA(serviceInformation.asXML());
					req.setDataElement(serviceInformation);
					
					if (logger.isInfoEnabled()) {
						logger.info("将请求消息转换成DCC消息对应格式的XmlMessage :"+serviceInformation.asXML());
					}
					
					BlackCardMsgIn blackCardMsgIn = new BlackCardMsgIn();
					blackCardMsgIn.setAreaCode(areaCode);
					blackCardMsgIn.setBillInformations(billInformations);
					blackCardMsgIn.setEndTime(endTime);
					blackCardMsgIn.setStartTime(startTime);
					blackCardMsgIn.setAccNbr(accNbr);
					blackCardMsgIn.setMid(mid);
					
					// new CallParam对象
					BlackCardHandler handler = new BlackCardHandler(body, blackCardMsgIn,blackCardConfigService,accNbr,billingMode);
					CallParam callParam = new CallParam(req.getMID(), req.getSCID(),req, req.getUID(), handler);
					BuzProcessorManager.getInstance().processRequest(callParam);
				}
			}catch (Exception e) {
				if (logger.isDebugEnabled()) {
					logger.debug(" IOException :"+ e.getMessage());
				}
				e.printStackTrace();
			}
			return ;
		}
		else{
			if (logger.isDebugEnabled()) {
				logger.debug(" request path is invalid:"	+ request.getPath().getPath()+ " , ignore this request.");
			}
		}
	}
	
	@Override
	public int sendRequest(CallParam callParam) {
		return 0 ;
	}

	@Override
	public void addPersist(Node node) {
		// TODO Auto-generated method stub

	}

	@Override
	public void deletePersist(Node node) {
		// TODO Auto-generated method stub

	}

	@Override
	public Set<String> monitorConfKeys() {
		return null;
	}

	@Override
	public void onConfigUpdated(String updatedKey) {
		// TODO Auto-generated method stub

	}
	
	/**
	 * @throws IOException 
	 * @throws UnsupportedEncodingException 
	 * 
	 * @MethodName: creatErrAnswer   
	 * @Description: 构造异常信息返回  
	 * @param @param responseStream
	 * @param @param retCode
	 * @param @param errMsg
	 * @return void  
	 * @throws
	 */
	private void creatErrAnswer(PrintStream responseStream, String mid,int retCode ,String accNbr,
			String servId,String retMsg
			) throws UnsupportedEncodingException, IOException 
	{
		StringBuilder sb = new StringBuilder();
		sb.append("<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">");
		sb.append("<SOAP-ENV:Header/>");
		sb.append("<SOAP-ENV:Body>");
		sb.append("<ns3:SgwQueryAck xmlns:ns3=\"http://www.chinatelecom.cn/SGW/SOAP\">");
		sb.append("<MID>%s</MID>");
		sb.append("<REPTAG>0</REPTAG>");
		sb.append("<RTT>20150317163021130</RTT>");
		sb.append("<SIG>null</SIG>");
		sb.append("<RC>0</RC>");
		sb.append("<DS>SUCCESS</DS>");
		sb.append("<DATA><![CDATA[");
		sb.append("<Service_Information>");
		sb.append("<Result>%s</Result>");         
		sb.append("<Acc_Nbr>%s</Acc_Nbr>");
		sb.append("<ServId>%s</ServId>");  
		sb.append("<Ret_Msg>%s</ Ret_Msg>");            
		sb.append("</Service_Information>]]>");                 
	    sb.append("</DATA>");
	    sb.append("</ns3:SgwQueryAck>");		
	    sb.append("</SOAP-ENV:Body>");
	    sb.append("</SOAP-ENV:Envelope>");	
		
	    String responseMsg = String.format(
	    		sb.toString(),
	    		StringUtils.isEmpty(mid)?"":mid,
	    		String.valueOf(retCode),
	    		StringUtils.isEmpty(accNbr)?"":accNbr,
				StringUtils.isEmpty(servId)?"":servId,	    
				StringUtils.isEmpty(retMsg)?"":retMsg
				);
	    
		responseStream.write(responseMsg.getBytes("UTF-8"));
		responseStream.close();
		
		if (logger.isInfoEnabled()) {
			logger.info("http response  " + sb.toString());
		}
	}

	public BlackCardConfigService getBlackCardConfigService() {
		return blackCardConfigService;
	}

	public void setBlackCardConfigService(BlackCardConfigService blackCardConfigService) {
		this.blackCardConfigService = blackCardConfigService;
	}

}